# -*- coding: utf-8 -*-
# created on 2017/05/05
from sympy import S, oo, Abs, solve, simplify, Add, Max
from mathsolver.functions.base.objects import BaseBelong, new_latex
from mathsolver.functions.sympy_utils import get_all_child


def _cal_teshudian(expr, var):
    """从表达式中找到计算最值的特殊点"""
    teshudian = []
    abs_exprs = get_all_child(expr, lambda x: isinstance(x, Abs))  # 表达式中的绝对值项

    for abs_expr in abs_exprs:
        _inner_expr = abs_expr.args[0]
        # Abs(f(x, a)) => 计算满足 f(x, a) = 0 的x值
        lingdian = solve(_inner_expr, var)
        teshudian.extend(lingdian)

    # 如果出现了两个或两个以上绝对值，不计算极值点
    if len(abs_exprs) == 1:
        abs_expr = abs_exprs[0]
        _inner_expr = abs_expr.args[0]
        # 绝对值取负号、正号: Abs(x^2-2ax+1) + 2x+1
        jizhi_expr1 = simplify(expr.subs(abs_expr, _inner_expr))  # +(x^2-2ax+1) + 2x+1
        jizhi_expr2 = simplify(expr.subs(abs_expr, -_inner_expr))  # -(x^2-2ax+1) + 2x+1
        # f'(x) = 0
        jizhidian1 = solve(jizhi_expr1.diff(var), var)
        jizhidian2 = solve(jizhi_expr2.diff(var), var)
        teshudian.extend(jizhidian1 + jizhidian2)

    teshudian = set(teshudian)  # 去重

    return teshudian


def is_abs(expr):
    """是否是 Abs(*) 格式"""
    return expr.func == Abs


def is_neg_abs(expr):
    """是否是 -Abs(*) 格式"""
    return (-expr).func == Abs


def is_abs_or_neg_abs(expr):
    """是否是 Abs(*) 或者 -Abs(*)格式"""
    return is_abs(expr) or is_neg_abs(expr)


def _is_special_format(expr, var):
    """是否是 |f(x) - a| +/- |f(x) -b|的格式 => left op right，如果是返回 fx, a, b, is_minus"""
    if expr.func == Add and len(expr.args) == 2:
        arg1, arg2 = expr.args
        if is_abs_or_neg_abs(arg1) and is_abs_or_neg_abs(arg2):
            if is_neg_abs(arg1) and is_abs(arg2):  # -||,|| 格式
                is_minus = True
                left, right = arg2, -arg1
            elif is_neg_abs(arg2) and is_abs(arg1):  # ||,-|| 格式
                is_minus = True
                left, right = arg1, -arg2
            elif is_abs(arg1) and is_abs(arg2):  # ||,|| 格式
                is_minus = False
                left, right = arg1, arg2
            elif is_neg_abs(arg1) and is_neg_abs(arg2):  # -||,-|| 格式
                is_minus = False
                left, right = -arg1, -arg2
            else:
                return False

            inner_left, inner_right = left.args[0], right.args[0]
            inner_left_other, inner_left_x = inner_left.as_independent(var)
            inner_right_other, inner_right_x = inner_right.as_independent(var)

            fx = inner_left_x
            a = -inner_left_other
            # |f(x) - a| +/- |f(x) -b|
            if inner_left_x == inner_right_x:
                b = -inner_right_other
            # |f(x) - a| +/- |-f(x) -b|
            elif inner_left_x == -inner_right_x:
                b = inner_right_other
            else:
                return False

            return fx, a, b, is_minus


def zuizhi_abs_hancan(min_max, func, dingyi_interval, canshu_symbol, canshu_interval):
    """计算含参数绝对值函数的最值
    
    :return 
        1. 结果不含参数： [min_max_val, (x_val, reacheable)]
        2. 结果含参数：{canshu_range: [min_max_val, (x_val, reacheable)], ... }"""

    steps = []
    expr, var = func.expression, func.var

    try:
        special_format = _is_special_format(expr, var)
    except Exception:
        special_format = False

    # 如果是 |f(x) - a| +/- |f(x) -b| 格式，使用不等式求最值
    if special_format:
        fx, a, b, is_minus = special_format
        # |f(x) - a| - |f(x) -b|
        if is_minus:
            zuixiaozhi = -Abs(a - b)
            zuidazhi = Abs(a - b)
            steps.append(["", "根据绝对不等式的性质有 |{a} - {b}| <= |{fx} - {a}| - |{fx} - {b}| <= |{a} - {b}|"
                         .format(a=new_latex(a), b=new_latex(b), fx=new_latex(fx))])
        # |f(x) - a| + |f(x) -b|
        else:
            zuixiaozhi = Abs(a - b)
            zuidazhi = oo
            steps.append(["", "根据绝对不等式的性质有 |{fx} - {a}| + |{fx} - {b}| >= |{a} - {b}|"
                         .format(a=new_latex(a), b=new_latex(b), fx=new_latex(fx))])
        if min_max == Max:
            result = [zuidazhi, [None, zuidazhi != oo]]
        else:
            result = [zuixiaozhi, [None, True]]

        return result, steps

    teshudian = _cal_teshudian(expr, var)
    steps.append(["", "根据绝对值函数图像，比较 %s 在区间 %s 上的大小得" %
                  (', '.join([str(item) for item in teshudian]), new_latex(dingyi_interval))])

    from mathsolver.functions.hanshu.zuizhi_new import (interval_bound_val, compare_values, compare_duandian_jizhi)

    # 1. 没有定义区间，直接比较特殊点值的大小
    if dingyi_interval == S.Reals:
        result = {}
        values_to_compare = interval_bound_val(func, dingyi_interval,
                                               BaseBelong([canshu_symbol, "\\in", canshu_interval]))
        for _dian in teshudian:
            values_to_compare.update({expr.subs(var, _dian): (_dian, True)})

        # 比较大小
        compare_result = compare_values(values_to_compare, canshu_symbol, canshu_interval, min_max)
        for _canshu_range, _zuizhi in compare_result:
            result.update({_canshu_range: [_zuizhi, values_to_compare[_zuizhi]]})

    # 2. 有定义区间，讨论特殊点是否在定义区间内部、外部
    else:
        other_values_to_compare = {}
        teshudian_hancan = []

        # 不含参数的点：直接比较大小
        for _dian in teshudian:
            if not _dian.has(canshu_symbol):
                if _dian in dingyi_interval:
                    other_values_to_compare.update({expr.subs(var, _dian): (_dian, True)})
            else:
                teshudian_hancan.append(_dian)

        # 含参数的点：讨论特殊点是否在区间内部、外部
        if len(teshudian_hancan) == 1:
            teshudian_hancan = teshudian_hancan[0]
        else:
            teshudian_hancan = set(teshudian_hancan)

        result = compare_duandian_jizhi(func, dingyi_interval, teshudian_hancan, canshu_symbol, canshu_interval,
                                        min_max, other_values_to_compare)

    # {(-1,oo): -a+4, (-oo, -1): a+6, (-oo, oo): -Abs(a+1)+5}
    if len(result) > 1 and S.Reals in result:
        zuizhi = result[S.Reals]
        result = {S.Reals: zuizhi}

        # {(-oo, oo): [Abs(a + 1), (a, True)]} => [Abs(a + 1), (a, True)]
    if len(result) == 1 and list(result.keys())[0] == S.Reals:
        result = list(result.values())[0]

    return result, steps


if __name__ == '__main__':
    pass
